package com.study.mike.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.study.mike.common.annotation.MyLog;
import com.study.mike.common.annotation.NeedDecrypt;
import com.study.mike.common.annotation.NeedEncrypt;
import com.study.mike.common.constants.RedisKey;
import com.study.mike.common.enums.MethodTypeEnum;
import com.study.mike.common.utils.BookUtil;
import com.study.mike.common.utils.EncryptUtils;
import com.study.mike.common.utils.MoneyUtil;
import com.study.mike.common.utils.QRCodeUtil;
import com.study.mike.model.dto.*;
import com.study.mike.model.entity.Book;
import com.study.mike.common.enums.BizCodeEnum;
import com.study.mike.common.exception.BizException;
import com.study.mike.mapper.BookMapper;
import com.study.mike.model.po.DeleteBookPO;
import com.study.mike.model.po.QueryBookPO;
import com.study.mike.model.po.SetBookStatusPO;
import com.study.mike.model.po.UpdateBookPO;
import com.study.mike.service.BookService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.lettuce.core.RedisException;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.task.TaskExecutor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.ResourceUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author KangHui
 * @since 2022-07-08
 */
@Service
@Slf4j
@CacheConfig(cacheNames = RedisKey.ONE_HOUR)
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {

    @Resource
    private BookMapper bookMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Value("${redis.book}")
    private String redisBookKey;

    @Resource
    private ObjectMapper objectMapper;

    @Resource(name = "threadPool")
    private TaskExecutor taskExecutor;


    @Override
    @Transactional(rollbackFor = Exception.class)
    @NeedEncrypt
    @CacheEvict(allEntries = true)
    public BookDTO addBook(Book book) {
        // 通过新增的书籍编号获取是否已经存在该编号的数据
        if (ObjectUtil.isNotNull(this.getBookInfo(book.getBookNo()))) {
            throw new BizException(BizCodeEnum.BOOK_NO_EXIT.getCode(), BizCodeEnum.BOOK_NO_EXIT.getMsg());
        }
        BookDTO bookDTO = new BookDTO();
        // 执行书籍新增
        if (this.save(book)) {
            BeanUtils.copyProperties(book, bookDTO);
        }
        return bookDTO;
    }

    @Override
    @NeedEncrypt
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    public BookDTO editBook(UpdateBookPO bookPO) {
        // 通过输入的书籍编号获取是否已经存在该编号的数据
        // 新增的时候过滤该数据的编号验证
        // 获取到该书籍的编号
        String bookerNo = bookMapper.selectById(bookPO.getId()).getBookNo();
        if (StrUtil.isEmpty(bookerNo)) {
            throw new BizException("400", "操作数据不存在！");
        }

        if (!ObjectUtil.equal(EncryptUtils.aesEncrypt(bookPO.getBookNo()), bookerNo) && ObjectUtil.isNotNull(this.getBookInfo((bookPO.getBookNo())))) {
            throw new BizException(BizCodeEnum.BOOK_NO_EXIT.getCode(), BizCodeEnum.BOOK_NO_EXIT.getMsg());
        }
        BookDTO bookDTO = new BookDTO();
        Book book = new Book();
        BeanUtils.copyProperties(bookPO, book);
        if (this.updateById(book)) {
            BeanUtils.copyProperties(book, bookDTO);
        }
        return bookDTO;
    }

    @Override
    @MyLog(title = "删除", methodType = MethodTypeEnum.DELETE)
    public Boolean deleteBook(DeleteBookPO bookPO) {
        return bookMapper.deleteById(12) > 0;
    }

    @Override
    @NeedDecrypt
    @NeedEncrypt
    @Cacheable(key = "#root.method.name + #bookPO.pageCurrent + #bookPO.pageSize")
    public Page<BookDTO> queryBook(QueryBookPO bookPO) {
        // 对分页查询特殊处理 每次选择不同查询参数的情况做缓存-----新增、编辑数据的时候清空value值为该查询的所有缓存
        LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StrUtil.isNotBlank(bookPO.getBookName()), Book::getBookName, bookPO.getBookName())
                .eq(StrUtil.isNotBlank(bookPO.getBookAuthor()), Book::getBookAuthor, (bookPO.getBookAuthor()))
                .eq(StrUtil.isNotBlank(bookPO.getBookNo()), Book::getBookNo, (bookPO.getBookNo()))
                .eq(ObjectUtil.isNotEmpty(bookPO.getBookPrice()), Book::getBookPrice, bookPO.getBookPrice())
                .eq(ObjectUtil.isNotEmpty(bookPO.getStatus()), Book::getStatus, bookPO.getStatus())
                .orderByDesc(Book::getCreateTime);
        Page<Book> page = new Page<>(bookPO.getPageCurrent(), bookPO.getPageSize());
        Page<Book> pages = (Page<Book>) this.page(page, wrapper);
        // 数据转化
        List<Book> bookList = pages.getRecords();
        List<BookDTO> bookDTOList = JSONUtil.toList(JSONUtil.toJsonStr(bookList), BookDTO.class);
        // 创建返回page对象集合
        Page<BookDTO> bookDTOPage = new Page<>(pages.getCurrent(), pages.getSize(), pages.getTotal());
        bookDTOPage.setRecords(bookDTOList);
        return bookDTOPage;
    }

    @Override
    public Boolean setStatus(SetBookStatusPO bookStatusPO) {
        Book book = new Book();
        BeanUtils.copyProperties(bookStatusPO, book);
        return this.updateById(book);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean importBook() throws Exception {
        // 1获取到导入的文件
        String filePath = "C:\\Users\\James\\Desktop\\20230324094027915.xlsx";
        taskExecutor.execute(new BookRunnable(filePath));
        return true;
    }

    @Override
    public Boolean exportBook() throws IOException {
        // todo 导出全部、导出当前页、导出所选
        // 获取查询到的书籍数据集合
        List<BookDTO> list = this.bookList();
        // 定义导出文件名
        String exportFileNa = "C:\\Users\\James\\Desktop\\" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + ".xlsx";
        // 使用easyExcel完成导出
        EasyExcel.write(exportFileNa, BookDTO.class).sheet().doWrite(list);
        log.info(exportFileNa + "文件导出成功");
        return true;
    }

    @Override
    public Boolean downloadBook() throws IOException, DocumentException {
        // 获取到书籍列表
        List<BookDTO> bookDTOS = this.bookList();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        // 设置下载文件的名称
        response.setHeader("Content-Disposition", "attachment;filename=book.pdf");
        // 创建文本对象
        Document document = new Document();
        PdfWriter.getInstance(document, response.getOutputStream());
        // document.open()
        document.open();
        // 中文解码
        BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        Font FontChinese = new Font(bfChinese, 12, Font.NORMAL);
        Font FontContext = new Font(bfChinese, 12, Font.BOLD);
        // 页数
        document.setPageCount(5);
        // 标题
        document.addTitle("书籍信息");
        // 作者
        document.addAuthor("james");
        // 主题
        document.addSubject("download book info");
        // 关键词
        document.addKeywords("download");
        // 创建者
        document.addCreator("james");
        //设置内容主体
        //标题列
        List<String> titleList = new ArrayList<>(10);
        titleList.add("编号");
        titleList.add("书籍名");
        titleList.add("书籍编号");
        titleList.add("书籍价格");
        titleList.add("书籍作者");
        titleList.add("出版时间");
        int i = 0;
        for (BookDTO bookDTO : bookDTOS) {
            PdfPTable tableContext = new PdfPTable(titleList.size());
            if (i == 0) {
                PdfPTable tableTitle = new PdfPTable(titleList.size());
                for (int j = 0; j <= 5; j++) {
                    PdfPCell pdfPCell = new PdfPCell();
                    pdfPCell.setPhrase(new Paragraph(titleList.get(j)));
                    pdfPCell.setBackgroundColor(BaseColor.LIGHT_GRAY);
                    pdfPCell.setHorizontalAlignment(Element.ALIGN_CENTER);
                    tableTitle.addCell(pdfPCell);
                }
                document.add(tableTitle);
            }
            PdfPCell cell = new PdfPCell();
            // 编号
            cell = new PdfPCell();
            cell.setPhrase(new Paragraph(String.valueOf(bookDTO.getId()), new Font(bfChinese)));
            tableContext.addCell(cell);
            document.add(tableContext);
            // 书籍名
            cell = new PdfPCell();
            cell.setPhrase(new Paragraph((bookDTO.getBookName()), new Font(bfChinese)));
            tableContext.addCell(cell);
            document.add(tableContext);
            //书籍编号
            cell = new PdfPCell();
            cell.setPhrase(new Paragraph((bookDTO.getBookNo()), new Font(bfChinese)));
            tableContext.addCell(cell);
            document.add(tableContext);
            // 书籍价格
            cell = new PdfPCell();
            cell.setPhrase(new Paragraph(String.valueOf(bookDTO.getBookPrice()), new Font(bfChinese)));
            tableContext.addCell(cell);
            document.add(tableContext);
            // 书籍作者
            cell = new PdfPCell();
            cell.setPhrase(new Paragraph((bookDTO.getBookAuthor()), new Font(bfChinese)));
            tableContext.addCell(cell);
            document.add(tableContext);
            //出版时间
            cell = new PdfPCell();
            cell.setPhrase(new Paragraph(String.valueOf(bookDTO.getBookTime()), new Font(bfChinese)));
            tableContext.addCell(cell);
            document.add(tableContext);
            i++;
        }
        document.close();
        return true;
    }

    /**
     * 通过书籍编号获取书籍信息
     *
     * @param bookNo
     * @return
     */
    private Book getBookInfo(String bookNo) {
        LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Book::getBookNo, bookNo);
        Book book = bookMapper.selectOne(wrapper);
        return book;
    }

    @Override
    @NeedEncrypt
    @Transactional(rollbackFor = Exception.class)
    public boolean saveOrUpdateBook(Book book) {
        try {
            this.saveOrUpdate(book);
        } catch (DuplicateKeyException e) {
            // 手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new BizException("400", "书籍编号已经存在！" + EncryptUtils.aesDecrypt(book.getBookNo()));
        }
        return true;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchBook() {
        List<Book> list = new ArrayList<>();
        for (int i = 0; i <= 3000; i++) {
            Book book = BookUtil.generateBook();
            list.add(book);
        }
        try {
            this.saveBatch(list);
        } catch (DuplicateKeyException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new BizException("400", "书籍编号已经存在！");
        }
        return true;
    }

    @Override
    public boolean synchronizationData() {
        // 获取到存量数据
        LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(Book::getId, 22, 4023);
        List<Book> bookList = this.list(wrapper);
        try {
            redisTemplate.opsForValue().set("book:info", bookList);
        } catch (RedisException e) {
            log.info("写入缓存异常");
            return false;
        }
        return true;
    }

    @Override
    public List<Book> redisBookInfo() throws JsonProcessingException {
        // 获取数据信息object对象
        Object object = redisTemplate.opsForValue().get(redisBookKey);
        String asToBeanString = objectMapper.writeValueAsString(object);
        // 字符串转化为实体
        List<Book> bookList = JSONUtil.toList(asToBeanString, Book.class);
        /*List<Book> bookList = JSON.parseArray(asToBeanString,Book.class);*/
        /* List<Book> bookList = JSONObject.parseArray(asToBeanString, Book.class);*/
        return bookList;
    }

    @Override
    public QrDTO createQr(CreateOrderDTO createOrderDTO) throws JsonProcessingException {
        QrDTO qrDTO = new QrDTO();
        // 处理0.12转化为0.11999999999999999555910790149937383830547332763671875的情况 -BigDecimal.valueOf()
        if (ObjectUtil.isAllNotEmpty(createOrderDTO.getOrderAmount(), createOrderDTO.getOrderId())) {
            // 生成二维码
            // 订单金额
            String orderAccountStr = MoneyUtil.changeUnit(createOrderDTO.getOrderAmount().toString());
            BigDecimal orderAccount = new BigDecimal(orderAccountStr);
            qrDTO.setOrderAmount(orderAccount);
            qrDTO.setOrderId(createOrderDTO.getOrderId().toString());
            String str = objectMapper.writeValueAsString(qrDTO);
            createOrderDTO.setMsg("success");
            qrDTO.setPayCode(QRCodeUtil.getBase64QRCode(str));
        }
        if (StrUtil.isEmpty(createOrderDTO.getMsg())) {
            throw new BizException("400", "生成支付二维码失败！");
        }
        return qrDTO;
    }

    @Override
    public String getIpAddress(String ip) {
        Searcher searcher = null;
        if ("127.0.0.1".equals(ip) || ip.startsWith("192.168")) {
            return "局域网 ip";
        }
        if (searcher == null) {
            try {
                File file = ResourceUtils.getFile("classpath:ipdb/ip2region.xdb");
                String dbPath = file.getPath();
                searcher = Searcher.newWithFileOnly(dbPath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String region = null;
        String errorMessage = null;
        try {
            region = searcher.search(ip);
        } catch (Exception e) {
            errorMessage = e.getMessage();
            if (errorMessage != null && errorMessage.length() > 256) {
                errorMessage = errorMessage.substring(0, 256);
                log.info(errorMessage);
            }
            e.printStackTrace();
        }
        return region;
    }

    /**
     * 书籍列表
     *
     * @return
     */
    @NeedDecrypt
    @Override
    public List<BookDTO> bookList() {
        List<Book> bookList = bookMapper.selectList(null);
        List<BookDTO> result = bookList.stream().map(x -> {
            BookDTO bookDTO = new BookDTO();
            BeanUtils.copyProperties(x, bookDTO);
            bookDTO.setBookNo(EncryptUtils.aesDecrypt(bookDTO.getBookNo()));
            bookDTO.setBookAuthor(EncryptUtils.aesDecrypt(bookDTO.getBookAuthor()));
            return bookDTO;
        }).collect(Collectors.toList());
        return result;
    }


}
